Object cleanup (#307)
authortsteven4 <tsteven4@users.noreply.github.com>
Wed, 27 Feb 2019 14:25:14 +0000 (07:25 -0700)
committerGitHub <noreply@github.com>
Wed, 27 Feb 2019 14:25:14 +0000 (07:25 -0700)
* elminiate some early program exits.

exits without cleanup make it harder to find memory leaks.

fix a few leaks.

enhance random format to support reading real time position data.

correct some sign related warnings.

* use invariant generator for random format.

* make compilers happy with random.

clang doesn't like:
  random.cc:61:42: note: read of non-constexpr variable 'generator' is not allowed in a constant expression
    constexpr double scalefactor = 1.0 / (*generator).max();

msvc doesn't like:
  warning C4100: 'fname': unreferenced formal parameter

* catch main.cc up with master manually.

12 files changed:
defs.h
main.cc
nmn4.cc
pocketfms_wp.cc
random.cc
reference/realtime.csv [new file with mode: 0644]
testo.d/realtime.test [new file with mode: 0644]
tpo.cc
unicsv.cc
vecs.cc
waypt.cc
xcsv.cc

diff --git a/defs.h b/defs.h
index 8801e75664427c640be6acb8a57383f8523101a0..19c3607bd2a3bcff4643b7e47dfe797a3a28202c 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -158,11 +158,11 @@ typedef enum {
   posndata
 } gpsdata_type;
 
-#define NOTHINGMASK            0
-#define WPTDATAMASK            1
-#define TRKDATAMASK            2
-#define        RTEDATAMASK             4
-#define        POSNDATAMASK            8
+#define NOTHINGMASK            0U
+#define WPTDATAMASK            1U
+#define TRKDATAMASK            2U
+#define        RTEDATAMASK             4U
+#define        POSNDATAMASK            8U
 
 /* mask objective testing */
 #define        doing_nothing (global_opts.masked_objective == NOTHINGMASK)
@@ -543,10 +543,7 @@ public:
   Waypoint();
   ~Waypoint();
   Waypoint(const Waypoint& other);
-  // the default assignment operator is not appropriate as we do deep copy of some members,
-  // and we haven't bothered to write an appropriate one.
-  // Catch attempts to use the default assignment operator.
-  Waypoint& operator=(const Waypoint& other) = delete;
+  Waypoint& operator=(const Waypoint& other);
 
   bool HasUrlLink() const;
   const UrlLink& GetUrlLink() const;
@@ -870,29 +867,29 @@ void setshort_is_utf8(short_handle h, int is_utf8);
 
 /* REQUIRED means that the option is required to be set.
  * See also BEGIN/END_REQ */
-#define ARGTYPE_REQUIRED   0x40000000
+#define ARGTYPE_REQUIRED   0x40000000U
 
 /* HIDDEN means that the option does not appear in help texts.  Useful
  * for debugging or testing options */
-#define ARGTYPE_HIDDEN     0x20000000
+#define ARGTYPE_HIDDEN     0x20000000U
 
 /* BEGIN/END_EXCL mark the beginning and end of an exclusive range of
  * options. No more than one of the options in the range may be selected
  * or set. If exactly one must be set, use with BEGIN/END_REQ
  * Both of these flags set is just like neither set, so avoid doing that. */
-#define ARGTYPE_BEGIN_EXCL 0x10000000
-#define ARGTYPE_END_EXCL   0x08000000
+#define ARGTYPE_BEGIN_EXCL 0x10000000U
+#define ARGTYPE_END_EXCL   0x08000000U
 
 /* BEGIN/END_REQ mark the beginning and end of a required range of
  * options.  One or more of the options in the range MUST be selected or set.
  * If exactly one must be set, use with BEGIN/END_EXCL
  * Both of these flags set is synonymous with REQUIRED, so use that instead
  * for "groups" of exactly one option. */
-#define ARGTYPE_BEGIN_REQ  0x04000000
-#define ARGTYPE_END_REQ    0x02000000
+#define ARGTYPE_BEGIN_REQ  0x04000000U
+#define ARGTYPE_END_REQ    0x02000000U
 
-#define ARGTYPE_TYPEMASK 0x00000fff
-#define ARGTYPE_FLAGMASK 0xfffff000
+#define ARGTYPE_TYPEMASK 0x00000fffU
+#define ARGTYPE_FLAGMASK 0xfffff000U
 
 #define ARG_NOMINMAX NULL, NULL
 #define ARG_TERMINATOR {0, 0, 0, 0, 0, ARG_NOMINMAX, NULL}
@@ -986,7 +983,7 @@ void disp_vec(const char* vecname);
 void init_vecs();
 void exit_vecs();
 void disp_formats(int version);
-const char* name_option(long type);
+const char* name_option(uint32_t type);
 void printposn(double c, int is_lat);
 
 void* xcalloc(size_t nmemb, size_t size);
diff --git a/main.cc b/main.cc
index 2d86a419377233a7dfcc710a17fcf7289444446d..2ccf26e62f40b104ea46a2d9bd6dde63d2e00fdc 100644 (file)
--- a/main.cc
+++ b/main.cc
 
  */
 
-#include <QtCore/QCoreApplication>
-#include <QtCore/QStack>
-#include <QtCore/QString>
-#include <QtCore/QTextCodec>
-#include <QtCore/QTextStream>
-
-#include "cet.h"
-#include "cet_util.h"
-#include "csv_util.h"
-#include "defs.h"
-#include "filterdefs.h"
-#include "inifile.h"
-#include "session.h"
-#include "src/core/file.h"
-#include "src/core/usasciicodec.h"
-#include <cctype>
-#include <clocale>
-#include <cstdio>
-#include <cstdlib>
-#include <csignal>
+#include <clocale>                  // for setlocale, LC_NUMERIC, LC_TIME
+#include <csignal>                  // for signal, SIGINT, SIG_ERR
+#include <cstdio>                   // for printf, fgetc, stdin
+#include <cstdlib>                  // for exit
+#include <cstring>                  // for strcmp
+#include <ctime>                    // for time
+
+#include <QtCore/QByteArray>        // for QByteArray
+#include <QtCore/QChar>             // for QChar
+#include <QtCore/QCoreApplication>  // for QCoreApplication
+#include <QtCore/QFile>             // for QFile
+#include <QtCore/QIODevice>         // for QIODevice::ReadOnly
+#include <QtCore/QLocale>           // for QLocale
+#include <QtCore/QStack>            // for QStack
+#include <QtCore/QString>           // for QString
+#include <QtCore/QStringList>       // for QStringList
+#include <QtCore/QSysInfo>          // for QSysInfo
+#include <QtCore/QTextCodec>        // for QTextCodec
+#include <QtCore/QTextStream>       // for QTextStream
+#include <QtCore/QtConfig>          // for QT_VERSION_STR
+#include <QtCore/QtGlobal>          // for qPrintable, qVersion, QT_VERSION, QT_VERSION_CHECK
+
 #ifdef AFL_INPUT_FUZZING
 #include "argv-fuzz-inl.h"
 #endif
 
+#include "defs.h"
+#include "cet_util.h"               // for cet_convert_init, cet_convert_strings, cet_convert_deinit, cet_deregister, cet_register, cet_cs_vec_utf8
+#include "csv_util.h"               // for csv_lineparse
+#include "filter.h"                 // for Filter
+#include "filterdefs.h"             // for disp_filter_vec, disp_filter_vecs, disp_filters, exit_filter_vecs, find_filter_vec, free_filter_vec, init_filter_vecs
+#include "inifile.h"                // for inifile_done, inifile_init
+#include "queue.h"                  // for queue
+#include "session.h"                // for start_session, session_exit, session_init
+#include "src/core/datetime.h"      // for DateTime
+#include "src/core/file.h"          // for File
+#include "src/core/usasciicodec.h"  // for UsAsciiCodec
+
 #define MYNAME "main"
 // be careful not to advance argn passed the end of the list, i.e. ensure argn < qargs.size()
 #define FETCH_OPTARG qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(argn+1) ? qargs.at(++argn) : QString()
 
-void signal_handler(int sig);
-
 class QargStackElement
 {
 public:
@@ -194,12 +206,16 @@ print_extended_info()
     "\n");
 }
 
-int
-main(int argc, char* argv[])
+static void
+signal_handler(int sig)
+{
+  (void)sig;
+  tracking_status.request_terminate = 1;
+}
+
+static int
+run(const char* prog_name)
 {
-#ifdef AFL_INPUT_FUZZING
-  AFL_INIT_ARGV();
-#endif
   int c;
   int argn;
   ff_vecs_t* ivecs = nullptr;
@@ -211,87 +227,17 @@ main(int argc, char* argv[])
   const char* ovec_opts = nullptr;
   const char* fvec_opts = nullptr;
   int opt_version = 0;
-  int did_something = 0;
-  const char* prog_name = argv[0]; /* argv is modified during processing */
+  bool did_something = false;
   queue* wpt_head_bak, *rte_head_bak, *trk_head_bak;   /* #ifdef UTF8_SUPPORT */
   signed int wpt_ct_bak, rte_ct_bak, trk_ct_bak;       /* #ifdef UTF8_SUPPORT */
-  QStack<QargStackElement> qargs_stack = QStack<QargStackElement>();
+  QStack<QargStackElement> qargs_stack;
 
-  // Create a QCoreApplication object to handle application initialization.
-  // In addition to being useful for argument decoding, the creation of a
-  // QCoreApplication object gets Qt initialized, especially locale related
-  // QTextCodec stuff.
-  // For example, this will get the QTextCodec::codecForLocale set
-  // correctly.
-  QCoreApplication app(argc, argv);
   // Use QCoreApplication::arguments() to process the command line.
   QStringList qargs = QCoreApplication::arguments();
 
-  (void) new gpsbabel::UsAsciiCodec(); /* make sure a US-ASCII codec is available */
-
-// MIN_QT_VERSION in configure.ac should correspond to the QT_VERSION_CHECK arguments in main.cc and gui/main.cc
-#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
-#error This version of Qt is not supported.
-#endif
-
-  // The first invocation of QTextCodec::codecForLocale() may result in LC_ALL being set to the native environment
-  // as opposed to the initial default "C" locale.
-  // This was demonstrated with Qt5 on Mac OS X.
-  // TODO: This need to invoke QTextCodec::codecForLocale() may be taken care of
-  // by creating a QCoreApplication.
-#ifdef DEBUG_LOCALE
-  printf("Initial locale: %s\n",setlocale(LC_ALL, NULL));
-#endif
-  (void) QTextCodec::codecForLocale();
-#ifdef DEBUG_LOCALE
-  printf("Locale after codedForLocale: %s\n",setlocale(LC_ALL, NULL));
-#endif
-  // As recommended in QCoreApplication reset the locale to the default.
-  // Note the documentation says to set LC_NUMERIC, but QCoreApplicationPrivate::initLocale()
-  // actually sets LC_ALL.
-  // Perhaps we should restore LC_ALL instead of only LC_NUMERIC.
-  if (strcmp(setlocale(LC_NUMERIC,nullptr), "C") != 0) {
-#ifdef DEBUG_LOCALE
-    printf("Resetting LC_NUMERIC\n");
-#endif
-    setlocale(LC_NUMERIC,"C");
-#ifdef DEBUG_LOCALE
-    printf("LC_ALL: %s\n",setlocale(LC_ALL, NULL));
-#endif
-  }
-  /* reset LC_TIME for strftime */
-  if (strcmp(setlocale(LC_TIME,nullptr), "C") != 0) {
-#ifdef DEBUG_LOCALE
-    printf("Resetting LC_TIME\n");
-#endif
-    setlocale(LC_TIME,"C");
-#ifdef DEBUG_LOCALE
-    printf("LC_ALL: %s\n",setlocale(LC_ALL, NULL));
-#endif
-  }
-
-  global_opts.objective = wptdata;
-  global_opts.masked_objective = NOTHINGMASK;  /* this makes the default mask behaviour slightly different */
-  global_opts.charset_name.clear();
-  global_opts.inifile = nullptr;
-
-  gpsbabel_now = time(nullptr);                        /* gpsbabel startup-time */
-  gpsbabel_time = current_time().toTime_t();                   /* same like gpsbabel_now, but freezed to zero during testo */
-
-  if (gpsbabel_time != 0) {    /* within testo ? */
-    global_opts.inifile = inifile_init(QString(), MYNAME);
-  }
-
-  init_vecs();
-  init_filter_vecs();
-  cet_register();
-  session_init();
-  waypt_init();
-  route_init();
-
   if (qargs.size() < 2) {
     usage(prog_name,1);
-    exit(0);
+    return 0;
   }
 
   /*
@@ -315,7 +261,7 @@ main(int argc, char* argv[])
       if (qargs.at(argn).size() > 2 && qargs.at(argn).at(2).toLatin1() == 'V') {
         print_extended_info();
       }
-      exit(0);
+      return 0;
     }
 
     if (qargs.at(argn).size() > 1 && (qargs.at(argn).at(1).toLatin1() == '?' || qargs.at(argn).at(1).toLatin1() == 'h')) {
@@ -324,7 +270,7 @@ main(int argc, char* argv[])
       } else {
         usage(prog_name,0);
       }
-      exit(0);
+      return 0;
     }
 
     c = qargs.at(argn).size() > 1 ? qargs.at(argn).at(1).toLatin1() : '\0';
@@ -364,7 +310,7 @@ main(int argc, char* argv[])
         fatal("Format does not support reading.\n");
       }
       if (global_opts.masked_objective & POSNDATAMASK) {
-        did_something = 1;
+        did_something = true;
         break;
       }
       /* simulates the default behaviour of waypoints */
@@ -382,7 +328,7 @@ main(int argc, char* argv[])
       cet_convert_strings(global_opts.charset, nullptr, nullptr);
       cet_convert_deinit();
 
-      did_something = 1;
+      did_something = true;
       break;
     case 'F':
       optarg = FETCH_OPTARG;
@@ -537,14 +483,14 @@ main(int argc, char* argv[])
      */
     case '^':
       disp_formats(opt_version);
-      exit(0);
+      return 0;
     case '%':
       disp_filters(opt_version);
-      exit(0);
+      return 0;
     case 'h':
     case '?':
       usage(prog_name,0);
-      exit(0);
+      return 0;
     case 'p':
       optarg = FETCH_OPTARG;
       inifile_done(global_opts.inifile);
@@ -590,7 +536,7 @@ main(int argc, char* argv[])
   if (qargs.size() > 2) {
     fatal("Extra arguments on command line\n");
   } else if ((!qargs.isEmpty()) && ivecs) {
-    did_something = 1;
+    did_something = true;
     /* simulates the default behaviour of waypoints */
     if (doing_nothing) {
       global_opts.masked_objective |= WPTDATAMASK;
@@ -625,7 +571,7 @@ main(int argc, char* argv[])
     }
   } else if (!qargs.isEmpty()) {
     usage(prog_name,0);
-    exit(0);
+    return 0;
   }
   if (ovecs == nullptr) {
     /*
@@ -710,7 +656,7 @@ main(int argc, char* argv[])
     if (ovecs && ovecs->position_ops.wr_deinit) {
       ovecs->position_ops.wr_deinit();
     }
-    exit(0);
+    return 0;
   }
 
 
@@ -718,6 +664,90 @@ main(int argc, char* argv[])
     fatal("Nothing to do!  Use '%s -h' for command-line options.\n", prog_name);
   }
 
+  return 0;
+}
+
+int
+main(int argc, char* argv[])
+{
+#ifdef AFL_INPUT_FUZZING
+  AFL_INIT_ARGV();
+#endif
+  int rc = 0;
+  const char* prog_name = argv[0]; /* may not match QCoreApplication::arguments().at(0)! */
+
+// MIN_QT_VERSION in configure.ac should correspond to the QT_VERSION_CHECK arguments in main.cc and gui/main.cc
+#if (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
+#error This version of Qt is not supported.
+#endif
+
+#ifdef DEBUG_LOCALE
+  printf("Initial locale: %s\n",setlocale(LC_ALL, NULL));
+#endif
+
+  // Create a QCoreApplication object to handle application initialization.
+  // In addition to being useful for argument decoding, the creation of a
+  // QCoreApplication object gets Qt initialized, especially locale related
+  // QTextCodec stuff.
+  // For example, this will get the QTextCodec::codecForLocale set
+  // correctly.
+  QCoreApplication app(argc, argv);
+
+  // The first invocation of QTextCodec::codecForLocale() or
+  // construction of QCoreApplication object
+  // may result in LC_ALL being set to the native environment
+  // as opposed to the initial default "C" locale.
+  // This was demonstrated with Qt5 on Mac OS X.
+#ifdef DEBUG_LOCALE
+  printf("Locale after initial setup: %s\n",setlocale(LC_ALL, NULL));
+#endif
+  // As recommended in QCoreApplication reset the locale to the default.
+  // Note the documentation says to set LC_NUMERIC, but QCoreApplicationPrivate::initLocale()
+  // actually sets LC_ALL.
+  // Perhaps we should restore LC_ALL instead of only LC_NUMERIC.
+  if (strcmp(setlocale(LC_NUMERIC,nullptr), "C") != 0) {
+#ifdef DEBUG_LOCALE
+    printf("Resetting LC_NUMERIC\n");
+#endif
+    setlocale(LC_NUMERIC,"C");
+#ifdef DEBUG_LOCALE
+    printf("LC_ALL: %s\n",setlocale(LC_ALL, NULL));
+#endif
+  }
+  /* reset LC_TIME for strftime */
+  if (strcmp(setlocale(LC_TIME,nullptr), "C") != 0) {
+#ifdef DEBUG_LOCALE
+    printf("Resetting LC_TIME\n");
+#endif
+    setlocale(LC_TIME,"C");
+#ifdef DEBUG_LOCALE
+    printf("LC_ALL: %s\n",setlocale(LC_ALL, NULL));
+#endif
+  }
+
+  (void) new gpsbabel::UsAsciiCodec(); /* make sure a US-ASCII codec is available */
+
+  global_opts.objective = wptdata;
+  global_opts.masked_objective = NOTHINGMASK;  /* this makes the default mask behaviour slightly different */
+  global_opts.charset_name.clear();
+  global_opts.inifile = nullptr;
+
+  gpsbabel_now = time(nullptr);                        /* gpsbabel startup-time */
+  gpsbabel_time = current_time().toTime_t();                   /* same like gpsbabel_now, but freezed to zero during testo */
+
+  if (gpsbabel_time != 0) {    /* within testo ? */
+    global_opts.inifile = inifile_init(QString(), MYNAME);
+  }
+
+  init_vecs();
+  init_filter_vecs();
+  cet_register();
+  session_init();
+  waypt_init();
+  route_init();
+
+  rc = run(prog_name);
+
   cet_deregister();
   waypt_flush_all();
   route_flush_all();
@@ -726,11 +756,5 @@ main(int argc, char* argv[])
   exit_filter_vecs();
   inifile_done(global_opts.inifile);
 
-  exit(0);
-}
-
-void signal_handler(int sig)
-{
-  (void)sig;
-  tracking_status.request_terminate = 1;
+  exit(rc);
 }
diff --git a/nmn4.cc b/nmn4.cc
index 476f9d71913dfad96ed33aef4ead894339514054..13237a58df5aa5e612b342d0b654ffa149ff2c22 100644 (file)
--- a/nmn4.cc
+++ b/nmn4.cc
@@ -90,7 +90,7 @@ nmn4_read_data()
 
     int column = -1;
     QString c = csv_lineparse(str, "|", "", column++);
-    while (c != nullptr) {
+    while (!c.isNull()) {
       switch (column) {
       case  0: /* "-" */       /* unknown fields for the moment */
       case  1: /* "-" */
index 603c4820d0ddc4230cec332b857e9bfd9b4df09e..dcaeea49fbda47c574b0fa2c7edec63770b9a5e4 100644 (file)
@@ -84,6 +84,9 @@ data_read()
     }
     wpt->longitude = wppos_to_dec(s);
     waypt_add(wpt);
+
+    // continue reading until csv_lineparse returns null indicating all dynamic memory has been deallocated.
+    while (csv_lineparse(nullptr, "\\w", "", linecount));
   }
 }
 
index dcfe680cb6e25ec959b892b06107a8edcd531961..303dfd5699a27fe91da05fc2298c779a739bfa04 100644 (file)
--- a/random.cc
+++ b/random.cc
     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
 */
 
+#include <cstdlib>           // for atoi
+#include <random>            // for mt19937
+
+#include <QtCore/QDateTime>  // for QDateTime
+#include <QtCore/QString>    // for QString
+#include <QtCore/QThread>    // for QThread
+
 #include "defs.h"
-#include "garmin_fs.h"
-#include "jeeps/gpsmath.h"
-#include <cstdlib>
-#include <ctime>
+#include "garmin_fs.h"       // for garmin_fs_t, GMSD_SET, garmin_fs_flags_t, garmin_fs_alloc
 
 #define MYNAME "random"
 
-static char* opt_points, *opt_seed;
+static char* opt_points, *opt_seed, *opt_nodelay;
 
 static arglist_t random_args[] = {
   {
@@ -37,20 +41,47 @@ static arglist_t random_args[] = {
     "seed", &opt_seed, "Starting seed of the internal number generator", nullptr,
     ARGTYPE_INT, "1", nullptr, nullptr
   },
+  {
+    "nodelay", &opt_nodelay, "Output realtime points without delay", nullptr,
+    ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+  },
   ARG_TERMINATOR
 };
 
+//  this generator is invariant across platforms.
+static std::mt19937* generator;
+
+// we do this cheesy distribution function because we need it to be invariant across platforms.
+// note uniform_int_distribution is not invariant.
+template <typename T>
+static T
+rand_num(const T max)
+{
+  T retval;
+  // scalefactor expression assumes generator is mt19937.
+  constexpr double scalefactor = 1.0 / std::mt19937::max();
+  do {
+    retval = static_cast<T>(static_cast<double>(max) * scalefactor * (*generator)());
+  } while (retval >= max);
+  return retval;
+}
 
 static double
 rand_dbl(const double max)
 {
-  return max * rand() / (((double)RAND_MAX) + 1);
+  return rand_num(max);
+}
+
+static float
+rand_flt(const float max)
+{
+  return rand_num(max);
 }
 
 static int
 rand_int(const int max)
 {
-  return (int)((double)max * rand() / (((double)RAND_MAX) + 1));
+  return rand_num(max);
 }
 
 /* rand_str always returns a valid string with len >= 0 */
@@ -60,7 +91,7 @@ rand_str(const int maxlen, const char* fmt)
 {
   int len = rand_int(maxlen) + 1;
 
-  char* res = (char*) xmalloc(len + 1);
+  auto res = (char*) xmalloc(len + 1);
   res[len] = '\0';
 
   for (int i = 0; i < len; i++) {
@@ -96,49 +127,26 @@ rand_qstr(const int maxlen, const char* fmt)
 static void
 random_rd_init(const QString&)
 {
+  generator = new std::mt19937;
+  if (opt_seed) {
+    generator->seed(atoi(opt_seed));
+  } else {
+    generator->seed(gpsbabel_now);
+  }
 }
 
 static void
 random_rd_deinit()
 {
+  delete generator;
 }
 
-static void
-random_read()
-{
 #define RND(a) (rand_int(a) > 0)
 
-  route_head* head;
-  Waypoint* prev = nullptr;
-  time_t time = gpsbabel_time;
-
-  if (opt_seed) {
-    srand(atoi(opt_seed));
-  } else {
-    srand(gpsbabel_now);
-  }
-
-
-  int points = (opt_points) ? atoi(opt_points) : rand_int(128) + 1;
-  if (doing_trks || doing_rtes) {
-    head = route_head_alloc();
-    if (doing_trks) {
-      head->rte_name = rand_qstr(8, "Trk_%s");
-      track_add_head(head);
-    } else {
-      head->rte_name = rand_qstr(8, "Rte_%s");
-      route_add_head(head);
-    }
-    head->rte_desc = rand_qstr(16, nullptr);
-       if RND(3) {
-      head->rte_urls.AddUrlLink(UrlLink(rand_qstr(8, "http://rteurl.example.com/%s")));
-    }
-  } else {
-    head = nullptr;
-  }
-
-  for (int i = 0; i < points; i++) {
-    Waypoint* wpt = new Waypoint;
+static Waypoint*
+random_generate_wpt(int i, const QDateTime& time, const Waypoint* prev)
+{
+    auto wpt = new Waypoint;
     garmin_fs_t* gmsd = garmin_fs_alloc(-1);
     fs_chain_add(&wpt->fs, (format_specific_data*) gmsd);
 
@@ -146,22 +154,22 @@ random_read()
       wpt->shortname = rand_qstr(8, "Wpt_%s");
     } while (wpt->shortname == nullptr);
 
-    wpt->latitude = rand_dbl(180) - 90;
-    wpt->longitude = rand_dbl(360) - 180;
+    wpt->latitude = rand_dbl(180.0) - 90.0;
+    wpt->longitude = rand_dbl(360.0) - 180.0;
 
     /* !!! "if RND(3) ..." produces some leaks in generated data !!! */
 
     if RND(3) {
-      wpt->altitude = rand_int(1000) / 10;
+      wpt->altitude = rand_dbl(100.0);
     }
     if RND(3) {
-      WAYPT_SET(wpt, temperature, rand_int(320) / 10.0);
+      WAYPT_SET(wpt, temperature, rand_flt(32.0f));
     }
     if RND(3) {
-      WAYPT_SET(wpt, proximity, rand_int(10000) / 10.0);
+      WAYPT_SET(wpt, proximity, rand_dbl(1000.0));
     }
     if RND(3) {
-      WAYPT_SET(wpt, depth, rand_int(10000) / 10.0);
+      WAYPT_SET(wpt, depth, rand_dbl(1000.0));
     }
     if RND(3) {
       wpt->AddUrlLink(rand_qstr(8, "http://link1.example.com/%s"));
@@ -174,22 +182,18 @@ random_read()
     }
 
     wpt->SetCreationTime(time);
-    if RND(3) {
-      wpt->creation_time = wpt->creation_time.addMSecs(rand_int(1000));
-    }
-    time += rand_int(10) + 1;
 
-    if (doing_trks) {
+    if (doing_trks || doing_posn) {
       if (i > 0) {
-        wpt->latitude = prev->latitude + (rand_dbl(1) / 1000);
-        wpt->longitude = prev->longitude + (rand_dbl(1) / 1000);
+        wpt->latitude = prev->latitude + rand_dbl(0.001);
+        wpt->longitude = prev->longitude + rand_dbl(0.001);
         WAYPT_SET(wpt, course, waypt_course(prev, wpt));
         WAYPT_SET(wpt, speed, waypt_speed(prev, wpt));
       }
       wpt->sat = rand_int(12 + 1);
-      wpt->hdop = (rand_int(500)) / 10.0;
-      wpt->vdop = (rand_int(500)) / 10.0;
-      wpt->pdop = (rand_int(500)) / 10.0;
+      wpt->hdop = rand_flt(50.0f);
+      wpt->vdop = rand_flt(50.0f);
+      wpt->pdop = rand_flt(50.0f);
       wpt->fix = (fix_type)(rand_int(6) - 1);
       if RND(3) {
         wpt->cadence = rand_int(255);
@@ -199,8 +203,8 @@ random_read()
       }
     } else {
       if (doing_rtes && (i > 0)) {
-        wpt->latitude = prev->latitude + (rand_dbl(1) / 100);
-        wpt->longitude = prev->longitude + (rand_dbl(1) / 100);
+        wpt->latitude = prev->latitude + rand_dbl(0.01);
+        wpt->longitude = prev->longitude + rand_dbl(0.01);
       }
       if RND(3) {
         wpt->description = rand_qstr(16, "Des_%s");
@@ -231,6 +235,37 @@ random_read()
       }
     }
 
+  return wpt;
+}
+
+static void
+random_read()
+{
+
+  route_head* head;
+  Waypoint* prev = nullptr;
+  QDateTime time = QDateTime::fromTime_t(gpsbabel_time);
+
+  int points = (opt_points) ? atoi(opt_points) : rand_int(128) + 1;
+  if (doing_trks || doing_rtes) {
+    head = route_head_alloc();
+    if (doing_trks) {
+      head->rte_name = rand_qstr(8, "Trk_%s");
+      track_add_head(head);
+    } else {
+      head->rte_name = rand_qstr(8, "Rte_%s");
+      route_add_head(head);
+    }
+    head->rte_desc = rand_qstr(16, nullptr);
+       if RND(3) {
+      head->rte_urls.AddUrlLink(UrlLink(rand_qstr(8, "http://rteurl.example.com/%s")));
+    }
+  } else {
+    head = nullptr;
+  }
+
+  for (int i = 0; i < points; i++) {
+    Waypoint* wpt = random_generate_wpt(i, time, prev);
     if (doing_trks) {
       track_add_wpt(head, wpt);
     } else if (doing_rtes) {
@@ -239,10 +274,65 @@ random_read()
       waypt_add(wpt);
     }
 
+    time = time.addMSecs(1000 + rand_int(10000));
     prev = wpt;
   }
 }
 
+struct realtime_data {
+  QDateTime time;
+  int points{-1};
+  int point_count{0};
+  Waypoint prev;
+};
+static realtime_data* realtime;
+
+void
+random_rd_posn_init(const QString&)
+{
+  generator = new std::mt19937;
+  if (opt_seed) {
+    generator->seed(atoi(opt_seed));
+  } else {
+    generator->seed(gpsbabel_now);
+  }
+  realtime = new realtime_data;
+  if (opt_points) {
+    realtime->points = atoi(opt_points);
+  }
+  realtime->time = QDateTime::fromTime_t(gpsbabel_time);
+}
+
+void
+random_rd_posn_deinit()
+{
+  delete generator;
+  delete realtime;
+}
+
+static Waypoint*
+random_rd_posn(posn_status* p_status)
+{
+  Waypoint* wpt = random_generate_wpt(realtime->point_count, realtime->time, &(realtime->prev));
+
+  if (p_status && (realtime->points > 0) && (realtime->point_count >= realtime->points)) {
+    p_status->request_terminate = 1;
+  }
+  ++realtime->point_count;
+
+  int delta_msecs= 1000 + rand_int(1000);
+  realtime->time = realtime->time.addMSecs(delta_msecs);
+  if (!opt_nodelay) {
+    QThread::msleep(delta_msecs);
+  }
+
+  // copy the waypoint as main will delete the returned waypt
+  // after write and we need it to generate the next wpt to
+  // simulate realtime tracking data.
+  realtime->prev = *wpt;
+
+  return wpt;
+}
 
 ff_vecs_t random_vecs = {
   ff_type_internal,
@@ -259,7 +349,10 @@ ff_vecs_t random_vecs = {
   nullptr,     /* write */
   nullptr,     /* exit */
   random_args,
-  CET_CHARSET_ASCII, 1                 /* fixed */
-  , NULL_POS_OPS,
+  CET_CHARSET_ASCII, 1,                        /* fixed */
+  {
+  random_rd_posn_init, random_rd_posn, random_rd_posn_deinit,
+  nullptr, nullptr, nullptr,
+  },
   nullptr
 };
diff --git a/reference/realtime.csv b/reference/realtime.csv
new file mode 100644 (file)
index 0000000..07a6518
--- /dev/null
@@ -0,0 +1,10 @@
+-28.606309,41.491196,85.918,Wpt_RD,1970-01-01T00:00:00Z
+-28.605513,41.492136,,Wpt_lBahVv7,1970-01-01T00:00:01.745Z
+-28.604906,41.492637,31.126,Wpt_hTE,1970-01-01T00:00:03.253Z
+-28.604548,41.493038,,Wpt_0H,1970-01-01T00:00:04.655Z
+-28.604396,41.493069,24.212,Wpt_k,1970-01-01T00:00:06.328Z
+-28.603524,41.493167,5.795,Wpt_iX,1970-01-01T00:00:07.835Z
+-28.603490,41.494039,36.201,Wpt_TyhlOke,1970-01-01T00:00:09.090Z
+-28.602885,41.494402,73.608,Wpt_rS,1970-01-01T00:00:10.507Z
+-28.602833,41.494822,2.947,Wpt_obZdF3b0,1970-01-01T00:00:11.965Z
+-28.601890,41.495138,,ESTIMATED Position,1970-01-01T00:00:13.543Z
diff --git a/testo.d/realtime.test b/testo.d/realtime.test
new file mode 100644 (file)
index 0000000..a5587e3
--- /dev/null
@@ -0,0 +1,15 @@
+
+echo 'DESCRIPTION realtime tracking 1' >>${TMPDIR}/realtime1.style
+echo 'EXTENSION csv' >>${TMPDIR}/realtime1.style
+echo 'FIELD_DELIMITER COMMA' >>${TMPDIR}/realtime1.style
+echo 'RECORD_DELIMITER NEWLINE' >>${TMPDIR}/realtime1.style
+echo 'IFIELD LON_DECIMAL,"","%f"' >>${TMPDIR}/realtime1.style
+echo 'IFIELD LAT_DECIMAL,"","%f"' >>${TMPDIR}/realtime1.style
+echo 'IFIELD ALT_METERS,"","%.3f"' >>${TMPDIR}/realtime1.style
+echo 'IFIELD SHORTNAME,"","%s"' >>${TMPDIR}/realtime1.style
+echo 'IFIELD ISO_TIME_MS,"","%s"' >>${TMPDIR}/realtime1.style
+# test real time tracking
+gpsbabel -T -i random,points=10,seed=22,nodelay -f dummy -o xcsv,style=${TMPDIR}/realtime1.style -F ${TMPDIR}/realtime.csv
+compare ${REFERENCE}/realtime.csv ${TMPDIR}/realtime.csv
+
+
diff --git a/tpo.cc b/tpo.cc
index 16a11d5b05c7802f7ea03557e6901c6d77963a35..2cff68ee375ac4a23cc0d86409c4085a8deaa3e2 100644 (file)
--- a/tpo.cc
+++ b/tpo.cc
@@ -747,13 +747,11 @@ static void tpo_process_tracks()
 
 
         if (buf[jj] == 0) {
-          printf("Found unexpected ZERO\n");
-          exit(1);
+          fatal(MYNAME ": Found unexpected ZERO\n");
         }
 
         if (latscale == 0 || lonscale == 0) {
-          printf("Found bad scales lonscale=0x%x latscale=0x%x\n", lonscale, latscale);
-          exit(1);
+          fatal(MYNAME ": Found bad scales lonscale=0x%x latscale=0x%x\n", lonscale, latscale);
         }
 
         lon+=lonscale*scarray[buf[jj]>>4];
index f937e6256ee64a895d5e133865bfd7379b77d665..9e90676747d9d58cd11f3e3d1540682beb1f4e3c 100644 (file)
--- a/unicsv.cc
+++ b/unicsv.cc
@@ -537,7 +537,7 @@ unicsv_fondle_header(QString s)
     cbuf = buf;
   }
 
-  while ((s = csv_lineparse(cbuf, unicsv_fieldsep, "\"", 0)), !s.isEmpty()) {
+  while ((s = csv_lineparse(cbuf, unicsv_fieldsep, "\"", 0)), !s.isNull()) {
     s = s.trimmed();
 
     field_t* f = &fields_def[0];
diff --git a/vecs.cc b/vecs.cc
index 4a72d018b6a5daf4df3c6859bf8bcb7b50a3cb32..de2e46fe513cf2e1c8a9f9419df4212b12998036 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
@@ -1442,7 +1442,7 @@ alpha(const void* a, const void* b)
 vecs_t**
 sort_and_unify_vecs(int* ctp)
 {
-  int vc;
+  size_t vc;
   vecs_t** svp;
 #if CSVFMTS_ENABLED
 #endif
@@ -1490,7 +1490,7 @@ sort_and_unify_vecs(int* ctp)
     }
     memset(&svp[i]->vec->cap, 0, sizeof(svp[i]->vec->cap));
     switch (xcsv_file.datatype) {
-    case 0:
+    case unknown_gpsdata:
     case wptdata:
       svp[i]->vec->cap[ff_cap_rw_wpt] = (ff_cap)(ff_cap_read | ff_cap_write);
       break;
@@ -1603,7 +1603,7 @@ disp_v2(ff_vecs_t* v)
 }
 
 const char*
-name_option(long type)
+name_option(uint32_t type)
 {
   const char* at[] = {
     "unknown",
index 65dfab024a16868a64b880b1c605c0b9b1fc5375..6a705c05cd4e0cb51060440d0b1c2759d9d5930a 100644 (file)
--- a/waypt.cc
+++ b/waypt.cc
 
  */
 
-#include "defs.h"
-#include "cet_util.h"
-#include "garmin_fs.h"
-#include "grtcirc.h"
-#include "session.h"
-#include "src/core/logging.h"
+#include <cmath>                // for fabs
+#include <cstdio>               // for printf, fflush, fprintf, stdout
+#include <ctime>                // for time_t
+
+#include <QtCore/QByteArray>    // for QByteArray
+#include <QtCore/QDateTime>     // for QDateTime
 #include <QtCore/QDebug>
-#include <QtCore/QList>
-#include <cmath>
-#include <cstdio>
+#include <QtCore/QList>         // for QList
+#include <QtCore/QString>       // for QString, operator==
+#include <QtCore/QTime>         // for QTime
+#include <QtCore/QtGlobal>      // for qPrintable
+
+#include "defs.h"
+#include "garmin_fs.h"          // for garmin_ilink_t, garmin_fs_s, GMSD_FIND, garmin_fs_p
+#include "grtcirc.h"            // for RAD, gcdist, heading_true_degrees, radtometers
+#include "queue.h"              // for queue, QUEUE_INIT, dequeue, QUEUE_FOR_EACH, QUEUE_MOVE, ENQUEUE_TAIL
+#include "session.h"            // for curr_session, session_t
+#include "src/core/datetime.h"  // for DateTime
+#include "src/core/logging.h"   // for Warning, Fatal
 
 #if NEWQ
 QList<Waypoint*> waypt_list;
@@ -256,11 +265,11 @@ waypt_compute_bounds(bounds* bounds)
 {
   waypt_init_bounds(bounds);
 #if NEWQ
-  foreach(Waypoint* waypointp, waypt_list) {
+  foreach (Waypoint* waypointp, waypt_list) {
 #else
   queue* elem, *tmp;
   QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
-    Waypoint* waypointp = reinterpret_cast<Waypoint *>(elem);
+    Waypoint* waypointp = reinterpret_cast<Waypoint*>(elem);
 #endif
     waypt_add_to_bounds(bounds, waypointp);
   }
@@ -270,12 +279,12 @@ Waypoint*
 find_waypt_by_name(const QString& name)
 {
 #if NEWQ
-  foreach(Waypoint* waypointp, waypt_list) {
+  foreach (Waypoint* waypointp, waypt_list) {
 #else
   queue* elem, *tmp;
 
   QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
-    Waypoint* waypointp = reinterpret_cast<Waypoint *>(elem);
+    Waypoint* waypointp = reinterpret_cast<Waypoint*>(elem);
 #endif
     if (waypointp->shortname == name) {
       return waypointp;
@@ -308,7 +317,7 @@ waypt_flush(queue* head)
   queue* elem, *tmp;
 
   QUEUE_FOR_EACH(head, elem, tmp) {
-    Waypoint* q = reinterpret_cast<Waypoint *>(dequeue(elem));
+    Waypoint* q = reinterpret_cast<Waypoint*>(dequeue(elem));
     delete q;
     if (head == &waypt_head) {
       waypt_ct--;
@@ -351,7 +360,7 @@ waypt_backup(signed int* count, queue** head_bak)
   waypt_ct = 0;
 
   QUEUE_FOR_EACH(qbackup, elem, tmp) {
-    wpt = reinterpret_cast<Waypoint *>(elem);
+    wpt = reinterpret_cast<Waypoint*>(elem);
     waypt_add(new Waypoint(*wpt));
     no++;
   }
@@ -536,7 +545,7 @@ waypt_gradient(const Waypoint* A, const Waypoint* B)
   }
 
   double altitude = A->altitude - B->altitude;
-  if (altitude == 0 || 
+  if (altitude == 0 ||
       A->altitude == unknown_alt || B->altitude == unknown_alt) {
     return 0;
   }
@@ -646,6 +655,69 @@ Waypoint::Waypoint(const Waypoint& other) :
   // note: extra_data is not deep copied.
 }
 
+Waypoint& Waypoint::operator=(const Waypoint& rhs)
+{
+  if (this != &rhs) {
+
+    // deallocate
+    if (gc_data != &Waypoint::empty_gc_data) {
+      delete gc_data;
+    }
+    fs_chain_destroy(fs);
+
+    // allocate and copy
+    // Q(rhs.Q),
+    latitude = rhs.latitude;
+    longitude = rhs.longitude;
+    altitude = rhs.altitude;
+    geoidheight = rhs.geoidheight;
+    depth = rhs.depth;
+    proximity = rhs.proximity;
+    shortname = rhs.shortname;
+    description = rhs.description;
+    notes = rhs.notes;
+    urls = rhs.urls;
+    wpt_flags = rhs.wpt_flags;
+    icon_descr = rhs.icon_descr;
+    creation_time = rhs.creation_time;
+    route_priority = rhs.route_priority;
+    hdop = rhs.hdop;
+    vdop = rhs.vdop;
+    pdop = rhs.pdop;
+    course = rhs.course;
+    speed = rhs.speed;
+    fix = rhs.fix;
+    sat = rhs.sat;
+    heartrate = rhs.heartrate;
+    cadence = rhs.cadence;
+    power = rhs.power;
+    temperature = rhs.temperature;
+    odometer_distance = rhs.odometer_distance;
+    gc_data = rhs.gc_data;
+    fs = rhs.fs;
+    session = rhs.session;
+    extra_data = rhs.extra_data;
+    // deep copy geocache data unless it is the specail static empty_gc_data.
+    if (rhs.gc_data != &Waypoint::empty_gc_data) {
+      gc_data = new geocache_data(*rhs.gc_data);
+    }
+
+    /*
+     * It's important that this duplicated waypoint not appear
+     * on the master Q.
+     */
+    QUEUE_INIT(&Q);
+
+    // deep copy fs chain data.
+    fs = fs_chain_copy(rhs.fs);
+
+    // note: session is not deep copied.
+    // note: extra_data is not deep copied.
+  }
+
+  return *this;
+}
+
 bool
 Waypoint::HasUrlLink() const
 {
diff --git a/xcsv.cc b/xcsv.cc
index fba18d3c716cf2772b659b6550df589365829124..eee9399f5bd83b40cce31cd75b217cab8c80aa22 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -1465,13 +1465,17 @@ xcsv_waypt_pr(const Waypoint* wpt)
       /* ALTITUDE CONVERSIONS**********************************************/
     case XT_ALT_FEET:
       /* altitude in feet as a decimal value */
-      buff = QString().sprintf(fmp.printfc.constData(),
-                METERS_TO_FEET(wpt->altitude));
+      if (wpt->altitude != unknown_alt) {
+        buff = QString().sprintf(fmp.printfc.constData(),
+                  METERS_TO_FEET(wpt->altitude));
+      }
       break;
     case XT_ALT_METERS:
       /* altitude in meters as a decimal value */
-      buff = QString().sprintf(fmp.printfc.constData(),
-                wpt->altitude);
+      if (wpt->altitude != unknown_alt) {
+        buff = QString().sprintf(fmp.printfc.constData(),
+                  wpt->altitude);
+      }
       break;
 
       /* DISTANCE CONVERSIONS**********************************************/